# @Time : 2020/8/1 11:04
# @Author : Fioman 
# @Phone : 13149920693
import cv2 as cv
import numpy as np
import os
import imutils


def image_show(name, image, time, show_state=False):
    if True:
        height, width = int(image.shape[0] / 6), int(image.shape[1] / 6)
        showImage = cv.resize(image, (width, height), interpolation=cv.INTER_AREA)
        cv.namedWindow(name, cv.WINDOW_AUTOSIZE)
        cv.imshow(name, showImage)
        cv.waitKey(0)


def get_width_height(filePathReal):
    """根据文件名获取图像的长宽尺寸,如果图像名称不对,就返回None"""
    if "w_" not in filePathReal or "h_" not in filePathReal:
        return None, None

    widthIndex = filePathReal.index("w_")
    heightIndex = filePathReal.index("h_")
    width = filePathReal[widthIndex + 2:widthIndex + 6]
    height = filePathReal[heightIndex + 2:heightIndex + 6]
    return int(width), int(height)


def findTotalBoard(img, width, height):
    image_show("Original", img, 0, True)
    """根据宽高,寻找整个板的物料区域,使用大律法"""
    boardArea = width * height
    T, thres = cv.threshold(img, np.mean(img[:150, :1100]) + 10, 255, cv.THRESH_BINARY)
    print("寻找物料区域的时候的阈值T = {},内小板面积 = {}".format(T, boardArea))
    image_show("TotalBoardThres", thres, 0, True)
    # 2.寻找轮廓,为了防止findcontous的返回值不一致的问题,可以使用imutils里面函数综合转换一下.
    contours = cv.findContours(thres, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    contours = imutils.grab_contours(contours)
    # 将找到的轮廓,中心点进行两两连线,防止断板的出现.
    largeContours = [contour for contour in contours if cv.contourArea(contour) > 0.2 * boardArea]
    print("找到的大的轮廓的个数 = {}".format(len(largeContours)))
    # 3.将所有的大轮廓,用白线连起来,再找一次物料区域
    for index, contour in enumerate(largeContours):
        centerFirst = int(cv.minAreaRect(contour)[0][0]), int(cv.minAreaRect(contour)[0][1])
        centerSecond = int(cv.minAreaRect(largeContours[(index + 1) % len(largeContours)])[0][0]), int(
            cv.minAreaRect((largeContours[(index + 1) % len(largeContours)]))[0][1])
        cv.line(thres, centerFirst, centerSecond, 255, 20)

    # 4.再找一次物料区域
    contours = cv.findContours(thres, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    contours = imutils.grab_contours(contours)

    contoursSorted = sorted(contours, key=cv.contourArea, reverse=True)
    contourMax = contoursSorted[0]
    print("最终找到的轮廓的面积 = {}".format(cv.contourArea(contourMax)))

    box = cv.minAreaRect(contourMax)
    (cx, cy), (w, h), angle = box
    if angle > 70:
        angle = 90 - angle
        box = ((cx, cy), (h, w), angle)
    if angle < -70:
        angle = 90 + angle
        box = ((cx, cy), (h, w), angle)
    boardMask = np.zeros(img.shape, dtype=np.uint8)
    cv.drawContours(boardMask, [contourMax], -1, 255, -1)
    image_show("BoardMask", boardMask, 0, True)
    imgGray = cv.bitwise_and(img, boardMask)
    image_show("BoardGray", imgGray, 0, True)
    print("轮廓面积 / 内板面积 = {} ".format(cv.contourArea(contourMax) / boardArea))

    if 1.8 * boardArea < cv.contourArea(contourMax) < 2.4 * boardArea:
        # 物料区域找对了
        return True, imgGray, box
    else:
        # 物料区域找错了
        return False, imgGray, box


def get_box_points_orderd(boxPoints):
    """将一个矩形框的四个点,按照左上,右上,右下,左下的顺序排列"""
    pointsSorted = sorted(boxPoints, key=np.sum)
    # 左上角和右下角肯定是一个最小,一个最大.


def handle_contour(closed, width, height, calcThres):
    """
    根据这个图计算识别出来的轮廓和给出的尺寸做验证和检测
    :param closed:
    :param width:
    :param height:
    :param calcThres:
    :return:
    """

    img = closed.copy()
    boardArea = width * height
    res = {"result": False, "upBox": None, "downBox": None, "info": "", "upCut": False, "downCut": False,
           "upWidthErr": -1, "upHeightErr": -1, "downWidthErr": -1, "downHeightErr": -1}

    cnts = cv.findContours(img, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    cntsSorted = sorted(cnts, key=cv.contourArea, reverse=True)
    s1, s2 = 0, 0
    try:
        if len(cntsSorted) == 0:
            res["result"] = "fail"
            res["info"] = "找不到板轮廓"
            return res
        _, (w1, h1), _ = cv.minAreaRect(cntsSorted[0])
        _, (w2, h2), _ = cv.minAreaRect(cntsSorted[1])
        s1 = w1 * h1
        s2 = w2 * h2
        # 如果只检测到一个大框,拆分成两个小框再试
        if s1 > 1.7 * boardArea:
            box = cv.minAreaRect(cntsSorted[0])
            boxPoints = np.int0(cv.boxPoints(box))
            lt, rt, rb, lb = get_box_points_orderd(boxPoints)



    except Exception:
        pass


def get_boxes_2(img, width, height, calcThres):
    """找到板的两个框"""
    rectWidth = int(120 * mm2pix)
    rectHeight = int(200 * mm2pix)
    if img is None:
        return False, "图像异常,请重拍!", None, None, None
    if len(img.shape) == 3:
        img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    # 先寻找物料区域
    res, gray, boardBox = findTotalBoard(img, width, height)
    # 如果物料区域找到了,先将板抠出来.
    if res:
        (cx, cy), (w, h), angle = boardBox
        widthHalf = w / 2
        heightHalf = h / 2
        # 将板抠出来,并且旋转成正矩形.
        M = cv.getRotationMatrix2D((cx, cy), angle, 1)
        # 如何扣板呢,制定一个规则.那就是确定起点,中心点坐标减去x或者y坐标.但是不能是负数.
        yStart = max(int(cy - heightHalf), 0)
        yEnd = min(int(cy + heightHalf), gray.shape[0])
        xStart = max(int(cx - widthHalf), 0)
        xEnd = min(int(cx + widthHalf), img.shape[1])
        totalBoardRotated = cv.warpAffine(gray, M, (gray.shape[1], gray.shape[0]))[yStart:yEnd, xStart:xEnd]
        jointLineMask = np.zeros(img.shape, dtype=np.uint8)
        jointLineMask[:, 1503 - 10:1503 + 10] = 255
        jointLineMaskRotated = cv.warpAffine(jointLineMask, M, (gray.shape[1], gray.shape[0]))[yStart:yEnd, xStart:xEnd]
        # 要处理的板图片,先用双边滤波,去除一些噪声
        imgDealed = totalBoardRotated
        blurred = cv.bilateralFilter(imgDealed,21,20,20)
        image_show("BilateralBlurred", blurred, 0, True)

        # 使用scharr算子计算边缘信息
        scharrX = cv.Scharr(blurred, ddepth=cv.CV_64F, dx=1, dy=0)
        scharrX = cv.convertScaleAbs(scharrX)
        scharrY = cv.Scharr(blurred, ddepth=cv.CV_64F, dx=0, dy=1)
        scharrY = cv.convertScaleAbs(scharrY)
        scharrXY = cv.addWeighted(scharrX, 0.5, scharrY, 0.5  , 0)
        image_show("ScharrX",scharrX,0,True)
        image_show("ScharrY",scharrY,0,True)

        dilated = None
        eroded = None
        image_show("ScharrXY", scharrXY, 0, True)
        iterTimes = 20
        kernel = cv.getStructuringElement(cv.MORPH_CROSS, (50, 50))
        # 先膨胀,后腐蚀
        for i in range(iterTimes):
            dilated = cv.dilate(scharrXY, kernel)
        for i in range(iterTimes):
            eroded = cv.erode(dilated, kernel)
        T, contourImg = cv.threshold(eroded, np.mean(eroded[int(h / 4 - 100):int(h / 4 + 100), int(w / 2 - 100):int(w / 2 + 100 )])+50, 255,
                                     cv.THRESH_BINARY)
        contourImg = cv.subtract(contourImg, jointLineMaskRotated)
        print("T = {}".format(T))
        image_show("Erode", contourImg, 0, True)

        mask = np.zeros((contourImg.shape[0] + 2, contourImg.shape[1] + 2), dtype=np.uint8)
        cv.floodFill(contourImg, mask, (contourImg.shape[1] // 2, contourImg.shape[0] // 4), 150)
        cv.floodFill(contourImg, mask, (contourImg.shape[1] // 2, contourImg.shape[0] * 3 // 4), 150)

        fillImg = np.zeros(contourImg.shape, dtype=np.uint8)
        fillImg[contourImg == 150] = 255
        image_show("FloodFill", fillImg, 0, True)

        # 进行闭操作,先腐蚀后膨胀.进行开运算
        closed = cv.morphologyEx(fillImg, cv.MORPH_OPEN, (17, 17), iterations=iterTimes)
        image_show("Closed", closed, 0, True)
        # 去掉四个角
        cv.rectangle(closed, (0, 0), (rectWidth, rectHeight), 0, -1)
        cv.rectangle(closed, (0, closed.shape[0]), (rectWidth, closed.shape[0] - rectHeight), 0, -1)
        cv.rectangle(closed, (closed.shape[1], 0), (closed.shape[1] - rectWidth, rectHeight), 0, -1)
        cv.rectangle(closed, (closed.shape[1], closed.shape[0]), (closed.shape[1] - rectWidth, closed.shape[0] - rectHeight), 0, -1)
        image_show("angleRemoved", closed, 0, True)
        res_postive = handle_contour(closed, width, height, calcThres)
        res = res_postive


def get_lines_with_boxes(img, upBox, downBox, mtx, topPix, leftPix, bottomPix, rightPix, width, height, totalBoard, param):
    pass


if __name__ == '__main__':
    calcThres = 20
    mm2pix = 3.2333
    edgePix = [10, 10, 10, 10]
    edgePix = [int(x * mm2pix) for x in edgePix]
    mtx = [[1, 0, 0], [0, 1, 0]]

    filePath = r"D:\chongda_raw\chongda_new_pic\08_07\378--2020-08-07_05-48-14--w_1978--h_1674.bmp"
    keepResPath = r"D:\aoshikang_raw_res_new"
    if not os.path.exists(keepResPath):
        os.mkdir(keepResPath)
    fileNames = []
    topPix, leftPix, bottomPix, rightPix = [x for x in edgePix]

    # 如果是文件
    if os.path.isfile(filePath):
        filePath, fileName = os.path.split(filePath)
        fileNames.append(fileName)
    else:
        for root, dirs, files in os.walk(filePath):
            for file in files:
                fileNames.append(file)

    # 遍历所有的文件
    for index, fileName in enumerate(fileNames):
        filePathReal = os.path.join(filePath, fileName)
        width, height = get_width_height(fileName)
        if width is None or height is None:
            print("*" * 10 + "第 {} 张图,图像文件名错误! 文件名: {}".format(index + 1, fileName) + "*" * 10)
            continue
        print("*" * 10 + "第 {} 张图 ({})".format(index + 1, fileName) + "*" * 10)
        img = cv.imread(filePathReal, cv.IMREAD_GRAYSCALE)

        res, info, upBox, downBox, totalBoard = get_boxes_2(img, width, height, int(calcThres * mm2pix))
        if res:
            assert upBox is not None
            assert downBox is not None
            resPathReal = os.path.join(keepResPath, fileName)
            cutLines, imgRes, cutDis = get_lines_with_boxes(img, upBox, downBox, mtx, topPix, leftPix, bottomPix, rightPix, width, height, totalBoard,
                                                            "铡刀")
